異常指的會是在程式碼執行期間,發生了不如預期
的錯誤,而不是單純的邏輯
上的錯誤,例如:
陣列的索引
進行操作時,誤將索引
寫成超出
陣列長度的情況,這就會出現ArrayIndexOfBoundsException(陣列索引超出範圍異常)
的狀況。邏輯錯誤
而非異常
。當發生異常而未進行處理時,JVM會因為錯誤而停止運作,Java中異常
的super class
是對應到java.lang.Throwable
,裡面又可分為兩個:
Error
:Java虛擬機無法處理的嚴重問題,一般不會寫針對性的程式碼進行處理這類型的問題(因為JVM會停止,只能針對程式碼進行修改)。例如:JVM系統內部錯誤、資源耗盡等的嚴重狀況。Exception
:在寫程式碼時有錯誤,或者是使用者操作發生偶然的錯誤。例如:ArrayIndexOfBoundsException(陣列索引超出範圍異常)
、讀取不存在的文件等。Exception
中又可分為兩種狀況:
編譯時
(受檢異常)出現異常
。運行時
(非受檢異常)出現異常
。常見運行時的異常:(以下程式碼省略最外層的部分,只寫核心的內容)
ArrayIndexOfBoundsException
:int[] arr = new int[10];
System.out.println(arr[10]); //ArrayIndexOfBoundsException
NullPointerException``空指針異常
:物件
還沒有實例化僅有記憶體中的位址值,這時去查詢物件內的資料。int[] arr = new int[10];
int = null;
System.out.println(arr[0]); //NullPointerException
ClassCastException
:當進行強制轉型時,型別不符合。String str = "hello";
Data date = (Date) str; //ClassCastException
NumberFormatException
:數字轉換不符合類型。String str = "123";
int i = Integer.parseInt(str); // 可以
str = "123a";
int i1 = Integer.parseInt(str); //NumberFormatException
InputMismatchException
:輸入的類型不符合對應類型。Scanner scan = new Scanner(System.in); //位在java.util底下的一個類,可以獲取使用者鍵盤輸入的內容
int i = scan.nextInt(); //如果使用者輸入數字可以將其賦值給變數
// 假設使用者輸入abc
System.out.println(i); //InputMismatchException
ArithmeticException
:算數異常。int i = 10;
System.out.println(i / 0); //ArithmeticException
編譯時
異常(必須在程式碼中另外處理)
可以使用try{} catch(Type err){} finally{}
的格式對可能出現異常的程式碼進行處理。
catch
比對異常類型)。catch
,每個catch
會有一個相對應的異常類型
,當出現異常時,會依次進行異常類型
的比對,當比對到相同的異常類型時,則會忽略其他的catch
。catch
時,在catch
參數中的異常類型
存在繼承
關係時,需要先從sub class
開始寫。
不論
是否有異常都會
執行的程式碼。public class ExceptionHandleTest {
public static void main(String[] args) {
try {
int i = 10;
System.out.println(i / 0);
} catch(ArithmeticException e) {
System.out.println("The wrong is being handled");
}
System.out.println("Hello");
}
}
The wrong is being handled
Hello
當有使用catch
將錯誤抓住的話,下面的Hello
就可以正常被執行出來,但是像上方的這種異常因為是屬於程式碼本身就有瑕疵
,所以需要去修改程式碼。
使用finally
public class ExceptionHandleTest {
public static void main(String[] args) {
try {
int i = 10;
System.out.println(i / 0);
} catch(ArithmeticException e) {
System.out.println("The wrong is being handled");
System.out.println(10 / 0);
} finally {
System.out.println("Finally");
}
System.out.println("Hello");
}
}
The wrong is being handled
Finally
假設catch
中也存在其他的異常
時,Hello
就不會被執行了,但是在finally
中的程式碼是不論怎麼樣都會執行(即使在try
或catch
中使用return
)。
在實際開發中,有些資源的操作必須要手動的進行,否則GC
不會主動的將它們回收,所以會將這些操作放在finally
中。例如:資料庫連接、Socket等等。
使用throws
異常的方式,如下method()
僅是將異常向上丟給了使用它的方法method1()
,但是在method1()
中,還是需要去處理這個異常的問題。
public void method1() {
try {
method();
} catch(FileNotFoundException err) {
err.printStackTrace(); // 此方法會在控制台中印出完整的錯誤訊息
}
}
public void method() throws FileNotFoundException {
}
前面在重寫Override
時有講過,sub class
重寫方法時,若是有throws
異常,異常的類型必須跟super class
相同或是該異常類型的sub class
,這是由於多態
的關係。
在實作接口時,若是接口的抽象方法沒有throws
則實作的方法也不能使用throws
。
interface Flyable {
public void fly() throws Exception;
}
class Plane implements Flyable {
@Override
public void fly() throws Exception {
}
}
異常處理的選擇try-catch-finally
or throws
?
try-catch-finally
:
super class
繼承的方法沒有寫throws
。throws
:
a()
→b()
→c()
依序連貫進行時,在a()
、b()
、c()
三個方法都會使用throws
的方式,並且在實際使用這三個方法的位置進行try-catch-finally
的處理。因為假設錯誤是發生在a()
,當直接在a()
中進行處理後,會繼續往b()
執行,但是b()
是依賴於a()
的正常執行,這時又會接續b()
和c()
的錯誤,所以會統一在同時使用a()
→b()
→c()
的方法中一次處理。throw
:可以透過使用throw new (異常類型物件)
的方式,手動將一個異常丟出如下:
public class ThrowTest {
public static void main(String[] args) {
Student student = new Student();
try {
student.register(-5);
System.out.println(student.id);
} catch(Exception err) {
err.printStackTrace(); // 輸入id非法
}
}
}
class Student {
int id;
public void register(int id) throws Exception {
if(id > 0) {
this.id = id;
} else {
throw new Exception("輸入id非法");
}
}
}
自定義異常類(可以參考jave.lang
中異常類的寫法)。
RuntimeException
或Exception
兩種。overload
的構造器。static final long serialVersionUID;
。使用自定義異常類最主要的重點在於能夠透過這個異常類的名稱,就知道具體發生的異常狀況是甚麼。